# 数组
"""
数组（Array）是一种线性表数据结构。它用一组连续的内存空间，来存储一组具有相同类型的数据。
    1.线性表（Linear List）。顾名思义，线性表就是数据排成像一条线一样的结构。每个线性表
    上的数据最多只有前和后两个方向。其实除了数组，链表、队列、栈等也是线性表结构。而与它
    相对立的概念是非线性表，比如二叉树、堆、图等。之所以叫非线性，是因为，在非线性表中，
    数据之间并不是简单的前后关系。
    2.连续的内存空间和相同类型的数据。正是因为这两个限制，它才有了一个堪称“杀手锏”的特
    性：“随机访问”。但有利就有弊，这两个限制也让数组的很多操作变得非常低效，比如要想在数
    组中删除、插入一个数据，为了保证连续性，就需要做大量的数据搬移工作。
"""
arr = [1, 2, 3, 4, 5, 6]


# 随机访问数组元素
"""
我们拿一个长度为 10 的 int 类型的数组 int[] a = new int[10] 来举例。计算机给数组 a[10]，
分配了一块连续内存空间 1000～1039，其中，内存块的首地址为 base_address = 1000。
我们知道，计算机会给每个内存单元分配一个地址，计算机通过地址来访问内存中的数据。当计算机需要随机访问数组中的某个元素时，
它会首先通过下面的寻址公式，计算出该元素存储的内存地址：
    a[i]_address = base_address + i * data_type_size   # data_type_size 表示数组中每个元素的大小。
因为数组是连续存储的，所以根据首地址和下标，通过寻址公式就能直接计算出对应的内存地址，找出数据。
"""


# 数组和链表的区别
"""
链表适合插入、删除，时间复杂度 O(1)；数组支持随机访问，根据下标随机访问的时间复杂度为 O(1)。
"""


# 数组插入和删除的低效
"""
前提：数组插入除尾部数据，删除头尾数据外
为了保持数组内存空间的连续性，插入和删除都会大量的迁移数据
"""


# 数组下标为什么从0开始
"""
从数组存储的内存模型上来看，“下标”最确切的定义应该是“偏移（offset）”。前面也讲到，
如果用 a 来表示数组的首地址，a[0]就是偏移为 0 的位置，也就是首地址，a[k]就表示偏移
 k 个 type_size 的位置，所以计算 a[k]的内存地址只需要用这个公式：
    a[k]_address = base_address + k * type_size
但是，如果数组从 1 开始计数，那我们计算数组元素 a[k]的内存地址就会变为：
    a[k]_address = base_address + (k-1)*type_size
对比两个公式，我们不难发现，从 1 开始编号，每次随机访问数组元素都多了一次减法运算，对于 CPU 来说，就是多了一次减法指令。
数组作为非常基础的数据结构，通过下标随机访问数组元素又是其非常基础的编程操作，效率的优化
就要尽可能做到极致。所以为了减少一次减法操作，数组选择了从 0 开始编号，而不是从 1 开始。
不过我认为，上面解释得再多其实都算不上压倒性的证明，说数组起始编号非 0 开始不可。
所以我觉得最主要的原因可能是历史原因。
C 语言设计者用 0 开始计数数组下标，之后的 Java、JavaScript 等高级语言都效仿了 C 语言，
或者说，为了在一定程度上减少 C 语言程序员学习 Java 的学习成本，因此继续沿用了从 0 开始
计数的习惯。实际上，很多语言中数组也并不是从 0 开始计数的，比如 Matlab。甚至还有一些语
言支持负数下标，比如 Python。
"""